/**
 * 
 */
package com.inspectortime.domain.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.management.InvalidAttributeValueException;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

import com.inspectortime.common.PersistentEntityBase;
import com.inspectortime.domain.HomeBuyer;
import com.inspectortime.domain.Inspection;
import com.inspectortime.domain.Inspector;
import com.inspectortime.domain.OptionalService;
import com.inspectortime.domain.Property;
import com.inspectortime.domain.RealEstateAgent;
import com.inspectortime.domain.type.InspectionStatus;
import com.inspectortime.domain.type.LockboxType;
import com.inspectortime.domain.type.PaymentStatus;
import com.inspectortime.domain.type.PresentAtInspectionType;
import com.inspectortime.timestamp.Timestamped;
import com.inspectortime.util.HashUtils;
import com.inspectortime.util.StringBufferUtils;
import com.mkode.util.java.lang.CollectionUtils;

@Entity
@Table(name = "INSPECTION")
public class InspectionImpl extends PersistentEntityBase implements Inspection, Timestamped {

	private static Log log = LogFactory
	.getLog(InspectionImpl.class);
	
	private String comments;
	
	private boolean electricityExpectedToBeOn;

	private double enteredInspectionFee;
	
	private boolean gasExpectedToBeOn;

	private HomeBuyer homeBuyer;

	private HomeBuyer secondHomeBuyer;

	private String inspectionCode;
	
	private double inspectionFee;

	private InspectionStatus inspectionStatus;
	
	private PaymentStatus paymentStatus;

	private Date inspectionTime;

	private Date createdTime;
	private Date lastModifiedTime;
	
	private Inspector inspector;

	private String lockboxRealtorCode;

	private LockboxType lockboxType;

	private List<OptionalService> optionalServices = new ArrayList<OptionalService>();

	private Property property;

	private RealEstateAgent referringAgent;

	private PresentAtInspectionType presentAtInspection;
	
	private boolean quickBookReminderHasBeenSent;
	
	private String requestUrl;
	
	private String listingAgentName;
	
	private String listingAgentPhoneNumber;
	
	private boolean isWebBooked;
	

	public String getRequestUrl() {
		return requestUrl;
	}

	public void setRequestUrl(String requestUrl) {
		this.requestUrl = requestUrl;
	}

	public boolean getQuickBookReminderHasBeenSent() {
		return quickBookReminderHasBeenSent;
	}

	public void setQuickBookReminderHasBeenSent(boolean quickBookReminderHasBeenSent) {
		this.quickBookReminderHasBeenSent = quickBookReminderHasBeenSent;
	}

	public PresentAtInspectionType getPresentAtInspection() {
		return presentAtInspection;
	}

	public void setPresentAtInspection(PresentAtInspectionType presentAtInspection) {
		this.presentAtInspection = presentAtInspection;
	}

	private boolean waterExpectedToBeOn;

	/**
	 * CGLib seems to want a default constructor
	 *
	 */
	public InspectionImpl() {
	}
	
	public InspectionImpl(HomeBuyer homeBuyer, Property property,
			Inspector inspector) {
		this.homeBuyer = homeBuyer;
		this.property = property;
		this.inspector = inspector;
	}
	
	public void generateInspectionCode() throws InvalidAttributeValueException {
		// we should always have the homeBuyer email at this point
		if (homeBuyer != null && StringUtils.hasText(homeBuyer.getEmail())){
			inspectionCode = HashUtils.getHash(homeBuyer.getEmail() + System.currentTimeMillis());	
		} else {
			throw new InvalidAttributeValueException("HomeBuyer's email is necessary to generate an inspection code");
		}
	}
	
	public String getComments() {
		return this.comments;
	}
	
	public double getEnteredInspectionFee() {
		return enteredInspectionFee;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#getHomeBuyer()
	 */
	@ManyToOne(targetEntity = HomeBuyerImpl.class, cascade = CascadeType.ALL )	
	public HomeBuyer getHomeBuyer() {
		return homeBuyer;
	}
	
	@ManyToOne(targetEntity = HomeBuyerImpl.class, cascade = CascadeType.ALL )
	public HomeBuyer getSecondHomeBuyer() {
		return secondHomeBuyer;
	}


	@Column( length = 100 )
	public String getInspectionCode() {
		return inspectionCode;
	}

	public double getInspectionFee() {
		return inspectionFee;
	}

	public InspectionStatus getInspectionStatus() {
		return inspectionStatus;
	}

	public Date getInspectionTime() {
		return inspectionTime;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#getInspector()
	 */
	@ManyToOne(targetEntity = InspectorImpl.class, cascade = {CascadeType.ALL} )
	public Inspector getInspector() {
		return inspector;
	}

	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#getLockboxRealtorCode()
	 */
	@Column( length = 20 )
	public String getLockboxRealtorCode() {
		return lockboxRealtorCode;
	}

	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#getLockboxType()
	 */
	public LockboxType getLockboxType() {
		return lockboxType;
	}
	
	@Transient
	public List<String> getOptionalServiceIds() {
		return CollectionUtils.extractStringList(this.getOptionalServices(), "id");
	}


	@ManyToMany(targetEntity = OptionalServiceImpl.class, cascade = {CascadeType.ALL})
	@JoinTable(
        name="inspection_optional_service",
        joinColumns=@JoinColumn(name="INSPECTION_id"),
        inverseJoinColumns=@JoinColumn(name="optionalServices_id")
    )
	public List<OptionalService> getOptionalServices() {
		return optionalServices;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#getProperty()
	 */
	@ManyToOne(targetEntity = PropertyImpl.class, cascade = {CascadeType.ALL} )
	public Property getProperty() {
		return property;
	}

	@ManyToOne(targetEntity = RealEstateAgentImpl.class, cascade = {CascadeType.ALL} )
	public RealEstateAgent getReferringAgent() {
		return referringAgent;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#isElectricityExpectedToBeOn()
	 */
	public boolean isElectricityExpectedToBeOn() {
		return electricityExpectedToBeOn;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#isGasExpectedToBeOn()
	 */
	public boolean isGasExpectedToBeOn() {
		return gasExpectedToBeOn;
	}
	
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#isWaterExpectedToBeOn()
	 */
	public boolean isWaterExpectedToBeOn() {
		return waterExpectedToBeOn;
	}
	
	public String getListingAgentName() {
		return listingAgentName;
	}

	public String getListingAgentPhoneNumber() {
		return listingAgentPhoneNumber;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#isWebBooked()
	 */
	public boolean isWebBooked() {
		return isWebBooked;
	}
	
	
	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#getFinalInspectionFee()
	 */
	@Transient
	public double getFinalInspectionFee() {
		if (this.enteredInspectionFee > 0) {
			return this.enteredInspectionFee;
		}
		return this.inspectionFee;
	}

	public void setComments(String comments) {
		this.comments = comments;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#setElectricityExpectedToBeOn(boolean)
	 */
	public void setElectricityExpectedToBeOn(boolean on) {
		electricityExpectedToBeOn = on;
	}

	public void setEnteredInspectionFee(double enteredInspectionFee) {
		this.enteredInspectionFee = enteredInspectionFee;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#setGasExpectedToBeOn(boolean)
	 */
	public void setGasExpectedToBeOn(boolean on) {
		gasExpectedToBeOn = on;
	}

	public void setHomeBuyer(HomeBuyer homeBuyer) {
		this.homeBuyer = homeBuyer;
	}
	
	public void setSecondHomeBuyer(HomeBuyer secondHomeBuyer) {
		this.secondHomeBuyer = secondHomeBuyer;
	}

	public void setInspectionCode(String inspectionCode) {
		this.inspectionCode = inspectionCode;
	}

	public void setInspectionFee(double inspectionFee) {
		this.inspectionFee = inspectionFee;
	}

	public void setInspectionStatus(InspectionStatus inspectionStatus) {
		this.inspectionStatus = inspectionStatus;
	}

	public void setInspectionTime(Date inspectionTime) {
		this.inspectionTime = inspectionTime;
	}

	public void setInspector(Inspector inspector) {
		this.inspector = inspector;
	}

	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#setLockboxRealtorCode(java.lang.String)
	 */
	public void setLockboxRealtorCode(String lockboxRealtorCode) {
		this.lockboxRealtorCode = lockboxRealtorCode;
	}

	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#setLockboxType(com.inspectortime.domain.type.LockboxType)
	 */
	public void setLockboxType(LockboxType lockboxType) {
		this.lockboxType = lockboxType;
	}

	public void setOptionalServices(List<OptionalService> optionalServices) {
		this.optionalServices = optionalServices;
	}

	public void setProperty(Property property) {
		this.property = property;
	}

	public void setReferringAgent(RealEstateAgent agent) {
		this.referringAgent = agent;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.Inspection#setWaterExpectedToBeOn(boolean)
	 */
	public void setWaterExpectedToBeOn(boolean on) {
		waterExpectedToBeOn = on;
	}
	
	public void setListingAgentName(String listingAgentName) {
		this.listingAgentName = listingAgentName;
	}

	public void setListingAgentPhoneNumber(String listingAgentPhoneNumber) {
		this.listingAgentPhoneNumber = listingAgentPhoneNumber;
	}
	
	
	/*
	 * (non-Javadoc)
	 * @see com.inspectortime.domain.Inspection#setWebBooked(boolean)
	 */
	public void setWebBooked(boolean isWebBooked) {
		this.isWebBooked = isWebBooked;
	}
	
	
	/**
	 * Note: The fields displayed here should match the fields used in the FeeCalculator implementations
	 */
	public String toString() {		
		StringBuffer sb = new StringBuffer();
		if (this.getProperty() != null) {
			StringBufferUtils.addNameValuePair(sb, "propertyType", "", this.getProperty().getPropertyType(), "");
			StringBufferUtils.addNameValuePair(sb, "foundationType", "", this.getProperty().getFoundationType(), "");
			StringBufferUtils.addNameValuePair(sb, "price", "$", this.getProperty().getListedPrice(), "");
			StringBufferUtils.addNameValuePair(sb, "size", "", this.getProperty().getSquareFeet(), "sqft");
			StringBufferUtils.addNameValuePair(sb, "beds", "", this.getProperty().getNumberOfBedrooms(), "");
			StringBufferUtils.addNameValuePair(sb, "baths", "", this.getProperty().getNumberOfBathrooms(), "");
			if (this.getProperty().getLocation() != null) {
				StringBufferUtils.addNameValuePair(sb, "zip", "", this.getProperty().getLocation().getZipCode(), "");	
			}			
		}
		
		if (sb.length() == 0) {
			
			// Default
			return "any zip, price and size";
		}
		return sb.toString();
	}

	public Date getCreatedTime() {
		return createdTime;
	}

	public Date getLastModifiedTime() {
		return lastModifiedTime;
	}

	public void setCreatedTime(Date created) {
		this.createdTime = created;	
	}

	public void setLastModifiedTime(Date modified) {
		this.lastModifiedTime = modified;
	}

	public PaymentStatus getPaymentStatus() {
		return paymentStatus;
	}

	public void setPaymentStatus(PaymentStatus paymentStatus) {
		this.paymentStatus = paymentStatus;
	}

}
